package com.spring.aop;

import com.spring.constants.Constants;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

/**
 * @author SpringWang
 * @date 2021/6/3
 */
public class ProxyFactory {

    private final AdviceSupport support;

    public ProxyFactory(AdviceSupport support) {
        this.support = support;
    }

    public Object getProxy() {
        int interfaceCount = support.getTargetClass().getInterfaces().length;
        return interfaceCount > 0 ? getJdkProxy() : getCglibProxy();
    }

    public Object getJdkProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), this.support.getTargetClass().getInterfaces(),
                (proxy, method, args) -> createProxy(method, args));
    }

    public Object getCglibProxy() {
        return Enhancer.create(support.getTargetClass(),
                (MethodInterceptor) (o, method, objects, methodProxy) -> createProxy(method, objects));
    }

    private Object createProxy(Method method, Object[] objects) throws Exception {
        Map<String, Advice> advices = support.getAdvices(method, support.getTargetClass());
        Object result = null;
        try {
            // 调用前置通知
            invokeAdvice(advices.get(Constants.BEFORE));
            // 执行目标方法
            result = method.invoke(support.getTarget(), objects);
            // 调用后置通知
            invokeAdvice(advices.get(Constants.AFTER));
        } catch (Exception e) {
            // 调用异常通知
            invokeAdvice(advices.get(Constants.AFTER_THROWING));
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 执行切面方法
     */
    private void invokeAdvice(Advice advice) {
        try {
            advice.getAdviceMethod().invoke(advice.getAspect());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
